﻿namespace DomainServices.Tools
{
    using System;
    using System.CodeDom;
    using System.CodeDom.Compiler;
    using System.Collections.Generic;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Text;

    internal class NotificationMethodGenerator
    {
        private string createdBaseName;
        private const IndentationLevel DefaultIndentLevel = IndentationLevel.Namespace;
        private string indent;
        private const string IndentString = "    ";
        private bool isCSharp;
        private Dictionary<string, CodeMethodInvokeExpression> methodInvokeExpressions;
        private Dictionary<string, CodeSnippetTypeMember> partialMethodSnippets;
        private CodeDomClientCodeGenerator proxyGenerator;

        public NotificationMethodGenerator(CodeDomClientCodeGenerator proxyGenerator) : this(proxyGenerator, IndentationLevel.Namespace)
        {
        }

        public NotificationMethodGenerator(CodeDomClientCodeGenerator proxyGenerator, IndentationLevel indentLevel)
        {
            this.methodInvokeExpressions = new Dictionary<string, CodeMethodInvokeExpression>();
            this.partialMethodSnippets = new Dictionary<string, CodeSnippetTypeMember>();
            this.indent = string.Empty;
            this.createdBaseName = "Created";
            this.proxyGenerator = proxyGenerator;
            this.isCSharp = proxyGenerator.IsCSharp;
            int num = (int) indentLevel;
            if (num < 0)
            {
                num = 2;
            }
            while (num-- > 0)
            {
                this.indent = this.indent + "    ";
            }
            this.AddMethodFor("Created", Resource.CommentOnCreated);
        }

        public void AddMethodFor(string baseMethodName, string comments)
        {
            CodeParameterDeclarationExpressionCollection parameters = new CodeParameterDeclarationExpressionCollection();
            this.AddMethodFor(baseMethodName, parameters, comments);
        }

        public void AddMethodFor(string baseMethodName, CodeParameterDeclarationExpression parameterDeclaration, string comments)
        {
            CodeParameterDeclarationExpressionCollection parameters = new CodeParameterDeclarationExpressionCollection();
            if (parameterDeclaration != null)
            {
                parameters.Add(parameterDeclaration);
            }
            this.AddMethodFor(baseMethodName, parameters, comments);
        }

        public void AddMethodFor(string baseMethodName, CodeParameterDeclarationExpressionCollection parameters, string comments)
        {
            if (!string.IsNullOrEmpty(baseMethodName) && !this.methodInvokeExpressions.ContainsKey(baseMethodName))
            {
                string methodName = "On" + baseMethodName;
                List<CodeArgumentReferenceExpression> list = new List<CodeArgumentReferenceExpression>();
                if ((parameters != null) && (parameters.Count > 0))
                {
                    foreach (CodeParameterDeclarationExpression expression in parameters)
                    {
                        list.Add(new CodeArgumentReferenceExpression(expression.Name));
                    }
                }
                this.methodInvokeExpressions.Add(baseMethodName, new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), methodName, list.ToArray()));
                CodeSnippetTypeMember member = this.CreateNotificationPartialMethod(baseMethodName, parameters);
                if (!string.IsNullOrEmpty(comments))
                {
                    member.Comments.AddRange(CodeGenUtilities.GetDocComments(comments, this.isCSharp));
                }
                this.partialMethodSnippets.Add(baseMethodName, member);
            }
        }

        private CodeSnippetTypeMember CreateNotificationPartialMethod(string methodName, CodeParameterDeclarationExpressionCollection parameters)
        {
            CodeMemberMethod member = new CodeMemberMethod {
                Attributes = MemberAttributes.Public | MemberAttributes.Final,
                Name = "On" + methodName
            };
            member.Parameters.AddRange(parameters);
            if (this.proxyGenerator.ClientProxyCodeGenerationOptions.UseFullTypeNames)
            {
                foreach (CodeParameterDeclarationExpression expression in parameters.Cast<CodeParameterDeclarationExpression>())
                {
                    SetGlobalTypeReference(expression.Type);
                }
            }
            StringBuilder stringBuilder = null;
            using (CodeDomProvider provider = CodeDomProvider.CreateProvider(this.isCSharp ? "CSharp" : "VisualBasic"))
            {
                using (StringWriter writer = new StringWriter(CultureInfo.CurrentCulture))
                {
                    provider.GenerateCodeFromMember(member, writer, new CodeGeneratorOptions());
                    stringBuilder = writer.GetStringBuilder();
                }
            }
            stringBuilder.Replace("\r\n", string.Empty);
            stringBuilder.Replace("ublic", "artial", 1, "ublic".Length);
            if (this.isCSharp)
            {
                int num = stringBuilder.ToString().LastIndexOf(')');
                stringBuilder.Remove(num + 1, (stringBuilder.Length - num) - 1);
                stringBuilder.Append(";");
            }
            else
            {
                stringBuilder.Insert(0, "Private ");
                int index = stringBuilder.ToString().IndexOf("End Sub", StringComparison.Ordinal);
                stringBuilder.Insert(index, "\r\n" + this.indent);
            }
            stringBuilder.Insert(0, this.indent);
            return new CodeSnippetTypeMember(stringBuilder.ToString());
        }

        public CodeExpressionStatement GetMethodInvokeExpressionStatementFor(string baseMethodName)
        {
            CodeExpressionStatement statement = null;
            if (!string.IsNullOrEmpty(baseMethodName) && this.methodInvokeExpressions.Keys.Contains<string>(baseMethodName))
            {
                statement = new CodeExpressionStatement(this.methodInvokeExpressions[baseMethodName]);
            }
            return statement;
        }

        private static void SetGlobalTypeReference(CodeTypeReference codeTypeReference)
        {
            codeTypeReference.Options = CodeTypeReferenceOptions.GlobalReference;
            foreach (CodeTypeReference reference in codeTypeReference.TypeArguments)
            {
                SetGlobalTypeReference(reference);
            }
            if (codeTypeReference.ArrayElementType != null)
            {
                SetGlobalTypeReference(codeTypeReference.ArrayElementType);
            }
        }

        public CodeMethodInvokeExpression OnCreatedMethodInvokeExpression
        {
            get
            {
                return this.methodInvokeExpressions[this.createdBaseName];
            }
        }

        public CodeTypeMemberCollection PartialMethodsSnippetBlock
        {
            get
            {
                string text = "\r\n" + this.indent;
                CodeSnippetTypeMember item = new CodeSnippetTypeMember();
                CodeSnippetTypeMember member2 = new CodeSnippetTypeMember(text);
                item.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, Resource.Region_ExtensibilityMethodDefinitions));
                member2.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, null));
                List<CodeSnippetTypeMember> list = new List<CodeSnippetTypeMember>(this.partialMethodSnippets.Values);
                list.Insert(0, item);
                list.Insert(list.Count, member2);
                return new CodeTypeMemberCollection(list.ToArray());
            }
        }
    }
}

